package com.gitee.mocoffee.misc;

import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Optional;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * 获取系统信息的工具类
 *
 * @author lijia
 */
public class SysinfoKit {
    public static final SysinfoKit me = new SysinfoKit();

    private SysinfoKit() {}

    private static final char[] HEX_CHARS = new char[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};


    /**
     * 获取内存使用量
     *
     * @return
     */
    public MemoryUsage loadHeap() {
        return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
    }

    /**
     * 获取可用核心数
     *
     * @return
     */
    public int loadAvailableCore() {
        return ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors();
    }

    /**
     * 获取系统负载
     *
     * @return
     */
    public double loadSystemLoad() {
        return ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage();
    }

    /**
     * 获取JVM进程号
     *
     * @return
     */
    public long loadJVMPid() {
        String processName = ManagementFactory.getRuntimeMXBean().getName();
        String processID = processName.substring(0, processName.indexOf('@'));
        return Long.parseLong(processID);
    }

    private static class EnumerationIterator<T> implements Iterator<T> {
        private Enumeration<T> enumeration;

        private EnumerationIterator(Enumeration<T> enumeration) {
            this.enumeration = enumeration;
        }

        @Override
        public boolean hasNext() {
            return enumeration.hasMoreElements();
        }

        @Override
        public T next() {
            return enumeration.nextElement();
        }
    }

    private <T> Stream<T> buildStream(Enumeration<T> enumeration) {
        return StreamSupport.stream(
                Spliterators.spliteratorUnknownSize(new EnumerationIterator<T>(enumeration), Spliterator.IMMUTABLE),
                false);
    }

    private String loadIP(Predicate<InetAddress> predicate) {
        try {
            return buildStream(NetworkInterface.getNetworkInterfaces())
                    .filter((NetworkInterface ni) -> {
                        try {
                            return !ni.isVirtual() && ni.isUp();
                        } catch (SocketException e) {
                            return false;
                        }
                    })
                    .map(NetworkInterface::getInetAddresses)
                    .map(this::buildStream)
                    .map(addressStream -> addressStream.filter(predicate).findAny())
                    .filter(Optional::isPresent)
                    .map(Optional::get)
                    .map(InetAddress::getHostAddress)
                    .findAny()
                    .orElse(null);
        } catch (SocketException e) {
            return null;
        }
    }

    /**
     * 获取内网网卡IP
     *
     * @return
     */
    public String loadLocalIPV4() {
        return loadIP((InetAddress address) -> address instanceof Inet4Address && address.isSiteLocalAddress());
    }

    /**
     * 获取外网网卡IP
     *
     * @return
     */
    public String loadNetIPV4() {
        return loadIP((InetAddress address) -> address instanceof Inet4Address && !address.isSiteLocalAddress()
                && !address.isLoopbackAddress());
    }

    private byte[] loadMacBytes(Predicate<InetAddress> predicate) {
        try {
            return buildStream(NetworkInterface.getNetworkInterfaces())
                    .filter((NetworkInterface ni) -> {
                        try {
                            return !ni.isVirtual() && ni.isUp();
                        } catch (SocketException e) {
                            return false;
                        }
                    })
                    .filter((NetworkInterface ni) -> buildStream(ni.getInetAddresses()).anyMatch(predicate))
                    .map((NetworkInterface ni) -> {
                        try {
                            return ni.getHardwareAddress();
                        } catch (SocketException e) {
                            return null;
                        }
                    })
                    .findAny()
                    .orElse(null);
        } catch (SocketException e) {
            return null;
        }
    }

    private String loadMac(Predicate<InetAddress> predicate) {
        byte[] hardware = loadMacBytes(predicate);
        if (hardware == null)
            return null;
        else {
            StringBuilder builder = new StringBuilder();
            int high, low;
            for (int x = 0; x < hardware.length; x++) {
                high = (hardware[x] & 0xff) >> 4;
                low = hardware[x] & 0b1111;
                builder.append(HEX_CHARS[high]).append(HEX_CHARS[low]);
            }
            return builder.toString();
        }
    }

    /**
     * 获取内网网卡物理地址
     *
     * @return
     */
    public byte[] loadLocalMacBytes() {
        return loadMacBytes((InetAddress address) -> address.isSiteLocalAddress());
    }

    /**
     * 获取内网网卡物理地址
     *
     * @return
     */
    public String loadLocalMac() {
        return loadMac((InetAddress address) -> address.isSiteLocalAddress());
    }

    /**
     * 获取外网网卡物理地址
     *
     * @return
     */
    public byte[] loadNetMacBytes() {
        return loadMacBytes((InetAddress address) -> !address.isSiteLocalAddress() && !address.isLoopbackAddress());
    }

    /**
     * 获取外网网卡物理地址
     *
     * @return
     */
    public String loadNetMac() {
        return loadMac((InetAddress address) -> !address.isSiteLocalAddress() && !address.isLoopbackAddress());
    }
}
